package core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import util.ExecutionTime;
import interfaces.*;

public class JobManager extends Thread implements IJobManager {

	// Tamanho inicial de um job que um worker recem criado irá receber
	private int initialJobWorkload = Config.INITIAL_JOB_WORKLOAD;

	// Define um procurador para o gerente
	private IManagerProxy managerProxy;

	// Define o gerente de resposta
	private IAnswerManager answerManager;
	
	// Define o gerente de estado
	private IStatusManager statusManager;

	// Define uma fila de trabalhos para o trabalhador
	private BlockingDeque<Request> requestList;

	// Lista de tarefas
	private List<IJob> jobsList;

	// Lista de tarefas tempor�rias
	private List<IJob> tempJobsList;
	
	// Define o identificador do gerente
	private String managerId;

	// Define um identificador para um trabalhador
	private String workerId;

	// Identificacao para qual trabalhador este gerente deve enviar trabalho
	private Request request;

	// Tamanho minimo de tarefa para um trabalhador
	private int jobMinSize;

	// tamanho maximo de tarefa para um trabalhador
	private int jobMaxSize;

	// Tamanho da requisao de trabalho
	private int requestSize;

	// Calcula o tempo de execucao
	@SuppressWarnings("unused")
	private ExecutionTime executionTime;

	private boolean running = true;

	public JobManager() {
		try {
			// Instancia uma nova lista sincronizada de tarefas
			this.jobsList = Collections.synchronizedList(new ArrayList<IJob>());
		
			// Instancia uma nova lista bloqueante de requisicoes de trabalho
			this.requestList = new LinkedBlockingDeque<Request>();
		
			this.executionTime = new ExecutionTime();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void run() {
		try {
			while (this.running) {
				this.request = this.requestList.take();
				this.workerId = this.request.getWorkerId();
				this.requestSize = this.request.getRequestSize();

				if (this.requestSize == 0) {
					this.jobMinSize = (int) (this.initialJobWorkload - (this.initialJobWorkload * 0.5));
					this.jobMaxSize = (int) (this.initialJobWorkload + (this.initialJobWorkload * 0.5));
				} else {
					this.jobMinSize = (int) (this.requestSize - (this.requestSize * 0.5));
					this.jobMaxSize = (int) (this.requestSize + (this.requestSize * 0.5));
				}

				// //////////////////////////////////////////////////////////////////////
				// System.out.println("JobManager (" + this.manager.getID + ") =>");
				// System.out.println("\t VOU ENVIAR MAIS TRABALHO PARA O: + worker.getID());
				// System.out.println("\t\t MINIMO: " + jobMinSize);
				// System.out.println("\t\t MAXIMO: " + jobMaxSize + "\n");
				// //////////////////////////////////////////////////////////////////////

				this.tempJobsList = this.jobsSelect(jobMinSize, jobMaxSize);
				
				// //////////////////////////////////////////////////////////////////////
				//System.out.println("JobManager (" + this.getID() + ") =>");
				//System.out.println("\t VOU ENVIAR UM BANDO DE JOBS PARA: " + this.workerId);
				//System.out.println("\t\t QTDE. DE JOBS: " + this.tempJobsList.size() + "\n");
				// //////////////////////////////////////////////////////////////////////
				Iterator<IJob> it = this.tempJobsList.iterator();
		         while (it.hasNext()) {
		                 IJob jj = it.next();
		                 this.getAnswerbManager().setSendJobList(jj.getID(), jj);
		                 this.getManagerProxy().newJobSend(jj, workerId);
						
		                 // //////////////////////////////////////////////////////////////////////
						System.out.println("JobManager (" + this.getID() + ") =>");
						System.out.println("\t Enviando Job: " + jj.getID() + " para o Worker: " + this.workerId + "\n");
						// //////////////////////////////////////////////////////////////////////       
		         }
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void newJob(String jobId, byte[] jobDescription) {
		// TODO Auto-generated method stub
		try {
			// ///////////////////////////////////////////////////////////////////////
			// System.out.println("Worker: " + this.getID() + " =>");
			// System.out.println("\t Recebi Msg. NEW_JOB \n");
			// ///////////////////////////////////////////////////////////////////////
			IJob job = new Fibo(jobDescription);
			job.setID(jobId);
			//this.jobsList.add(job);
			this.setJobList(job);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public List<IJob> jobsSelect(int min, int max) {
		// TODO Auto-generated method stub
		try {
			List<IJob> tempJobsList = new ArrayList<IJob>();
			int tempJobsListSize = 0;
			
			while (!this.jobsList.isEmpty() && tempJobsListSize < min) {
				//IJob tempJob = this.jobsList.remove(0);
				IJob tempJob = this.getJobList();
				
				////////////////////////////////////////////////////////////////////////
				System.out.println("JobManager (" + this.getID() + ") =>");
				System.out.println("\t\t Removi o JOB: " + tempJob.getID() + "\n");
				//System.out.println("\t JOB SELECT");
				//System.out.println("\t MIN: " + min);
				//System.out.println("\t MAX: " + max);
				//System.out.println("\t TAMANHO: " + tempJob.getWorkload() + "\n");
				////////////////////////////////////////////////////////////////////////

				if (tempJobsListSize + tempJob.getWorkload() > max) {
					//this.setParentJobList(tempJob.getID(), tempJob);
					this.getAnswerbManager().setParentJobList(tempJob.getID(), tempJob);
					this.setAllJobList(tempJob.breakJob());
					////////////////////////////////////////////////////////////////////////
					System.out.println("JobManager (" + this.getID() + ") =>");
					System.out.println("\t\t Quebrei o JOB: " + tempJob.getID() + "\n");
				} else {
					
					System.out.println("JobManager (" + this.getID() + ") =>");
					System.out.println("\t\t Adicionei na lista para enciar: " + tempJob.getID() + "\n");
					
					tempJobsListSize += tempJob.getWorkload();
					tempJobsList.add(tempJob);
				}
			}
			////////////////////////////////////////////////////////////////////////
			//System.out.println("JobManager (" + this.manager.getID() + ") =>");
			//System.out.println("\t Vou enviar um bando de jobs com tamanho: " + tempJobsListSize +"\n");
			////////////////////////////////////////////////////////////////////////
			return tempJobsList;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public synchronized void setJobList(IJob job) {
		try {
			this.jobsList.add(job);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void setAllJobList(List<IJob> list) {
		try {
			this.jobsList.addAll(0, list);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public synchronized IJob getJobList(){
		try {
			//return this.sendJobsList.remove(jobId);
			return this.jobsList.remove(0);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	@Override
	public synchronized void newRequest(Request request) {
		// TODO Auto-generated method stub
		try {
			this.requestList.put(request);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void setID(String id) {
		// TODO Auto-generated method stub
		this.managerId = id;
	}

	@Override
	public String getID() {
		// TODO Auto-generated method stub
		return this.managerId;
	}
	
	@Override
	public IManagerProxy getManagerProxy() {
		// TODO Auto-generated method stub
		return this.managerProxy;
	}

	@Override
	public void setManagerProxy(IManagerProxy managerProxy) {
		// TODO Auto-generated method stub
		this.managerProxy = managerProxy;
	}

	@Override
	public IAnswerManager getAnswerbManager() {
		// TODO Auto-generated method stub
		return this.answerManager;
	}

	@Override
	public void setAnswerManager(IAnswerManager answerManager) {
		// TODO Auto-generated method stub
		this.answerManager = answerManager;
	}

	@Override
	public IStatusManager getStatusManager() {
		// TODO Auto-generated method stub
		return this.statusManager;
	}

	@Override
	public void setStatusMManager(IStatusManager statusManager) {
		// TODO Auto-generated method stub
		this.statusManager = statusManager;
	}
}
